package docker_client

import (
	"context"
	"fmt"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	"github.com/nacos-group/nacos-sdk-go/v2/inner/uuid"
	"go.uber.org/zap"
	"virtual-resource-schedule-service/model/docker_container"
)

// IDockerClient Docker的Client客户端
type IDockerClient interface {
	CreateDockerContainer(request *docker_container.DockerContainerRequest) (
		response *docker_container.DockerContainerResponse, err error)
	DockerContainerStart(request *docker_container.DockerContainerStartRequest) (
		response *docker_container.DockerContainerStartResponse, err error)
	DockerContainerStop(request *docker_container.DockerContainerStopRequest) (
		response *docker_container.DockerContainerStopResponse, err error)
}

type DockerClient struct {
	DockerClientWithOps *client.Client
}

func NewDockerClient(DockerClientWithOps *client.Client) *DockerClient {
	zap.S().Info("NewDockerClient created ... ...")
	return &DockerClient{
		DockerClientWithOps: DockerClientWithOps,
	}
}

func (dockerClient *DockerClient) CreateDockerContainer(request *docker_container.DockerContainerRequest) (
	response *docker_container.DockerContainerResponse, err error) {
	ctx := context.Background()
	uuid, err := uuid.NewV4()
	if err != nil {
		zap.S().Error(err)
	}
	containerName := fmt.Sprintf("%s-%s-%s", request.ContainerName, request.UserId, uuid.String())

	// 基础参数设置
	config := &container.Config{
		Image: request.ImageName,
		Env: []string{
			"SSH_PASSWORD=" + request.SSHPassword,
		},
		Labels: map[string]string{
			"name": containerName,
		},
	}
	// host信息设置
	hostPath := fmt.Sprintf("/data/user-data/%s/%s", request.ContainerName, string(request.UserId))
	hostConfig := &container.HostConfig{
		StorageOpt: map[string]string{
			"size": request.DataDiskSize,
		},
		Binds: []string{
			fmt.Sprintf("%s:/home/jovyan/work/jishuai/fs", hostPath), // 将宿主机的目录挂载到容器内的指定路径
		},
		RestartPolicy: container.RestartPolicy{
			Name: "unless-stopped",
		},
		Resources: container.Resources{
			// @TODO
			CPUShares: 4,
		},
		PortBindings: nat.PortMap{
			"8888/tcp": []nat.PortBinding{
				{
					HostIP:   "0.0.0.0",
					HostPort: "0",
				},
			},
			"22/tcp": []nat.PortBinding{
				{
					HostIP:   "0.0.0.0",
					HostPort: "0",
				},
			},
		},
		PublishAllPorts: true,
		Privileged:      true,
	}
	rsp, err := dockerClient.DockerClientWithOps.ContainerCreate(
		ctx, config, hostConfig, nil, nil, containerName)
	if err != nil {
		zap.S().Error(err)
	}
	zap.S().Infof("Create docker container success , response : %v", rsp)
	//UserId             string `json:"userId"`
	//MachineId          string `json:"machineId"`
	//ImageName          string `json:"imageName"`
	//ContainerName      string `json:"containerName"`
	//GPUCount           int    `json:"gpuCount"`
	containerResponse := docker_container.DockerContainerResponse{
		UserId:        request.UserId,
		MachineId:     request.MachineId,
		ImageName:     request.ImageName,
		ContainerName: containerName,
		GPUCount:      request.GPUCount,
		ContainerId:   rsp.ID,
	}
	return &containerResponse, err
}

func (dockerClient *DockerClient) DockerContainerStart(request *docker_container.DockerContainerStartRequest) (
	response *docker_container.DockerContainerStartResponse, err error) {
	ctx := context.Background()
	containerId := request.ContainerId
	err = dockerClient.DockerClientWithOps.ContainerStart(ctx, containerId, container.StartOptions{})
	if err != nil {
		zap.S().Error(err)
	}
	inspRes, err := dockerClient.DockerClientWithOps.ContainerInspect(ctx, containerId)
	jupyterHostPost := inspRes.NetworkSettings.Ports["8888/tcp"][0]
	// @TODO
	//sshHostPost := inspRes.NetworkSettings.Ports["22/tcp"][0]
	statusResponse := docker_container.DockerContainerStartResponse{
		UserId:         request.UserId,
		MachineId:      request.MachineId,
		ContainerName:  request.ContainerName,
		GPUCount:       request.GPUCount,
		Success:        true,
		SSHUrlPath:     "",
		SSHPort:        "1234",
		JupyterLabUrl:  jupyterHostPost.HostIP,
		JupyterLabPort: jupyterHostPost.HostPort,
	}
	return &statusResponse, err
}

func (dockerClient *DockerClient) DockerContainerStop(request *docker_container.DockerContainerStopRequest) (response *docker_container.DockerContainerStopResponse, err error) {
	ctx := context.Background()
	containerId := request.ContainerId
	err = dockerClient.DockerClientWithOps.ContainerStop(ctx, containerId, container.StopOptions{})
	if err != nil {
		zap.S().Error(err)
	}
	stopResponse := docker_container.DockerContainerStopResponse{
		UserId:        request.UserId,
		MachineId:     request.MachineId,
		ContainerName: request.ContainerName,
		ContainerId:   containerId,
	}
	return &stopResponse, err
}
